package vip.redme.memory.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.redme.memory.context.UserContext;
import vip.redme.memory.enums.ReviewedType;
import vip.redme.memory.pojo.dto.CardDetail;
import vip.redme.memory.pojo.entity.Card;
import vip.redme.memory.pojo.entity.Plan;
import vip.redme.memory.pojo.entity.Review;
import vip.redme.memory.pojo.entity.base.BaseEntity;
import vip.redme.memory.pojo.vo.CardSaveVo;
import vip.redme.memory.repository.CardRepository;
import vip.redme.memory.repository.PlanRepository;
import vip.redme.memory.repository.ReviewRepository;
import vip.redme.memory.service.CardManageService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import static vip.redme.memory.exception.BizException.of;
import static vip.redme.memory.response.BaseResultCode.RECORD_NOT_EXIST;

/**
 * CardManageServiceImpl
 *
 * @author HUZHAOYANG
 * @version 1.0
 * @date 2021/8/30 15:33
 */
@Service
@Slf4j
@Transactional
public class CardManageServiceImpl implements CardManageService {

    private static final String PLAN_PROCESSING_FORMAT = "{}/{}";
    @Autowired
    private CardRepository cardRepository;
    @Autowired
    private ReviewRepository reviewRepository;
    @Autowired
    private PlanRepository planRepository;


    @Override
    public void createCard(CardSaveVo cardSaveVo) {
        Card card = BeanUtil.toBean(cardSaveVo, Card.class);
        card.setUserId(UserContext.getUserId());
        card.setCheckTimes(1);
        card.setReviewedTimes(0);
        card = cardRepository.saveAndFlush(card);
        //generate one Review record.
        generateReviewForCreateCard(card);
    }

    /**
     * 删除Card
     *
     * @param id
     * @return void
     * @author HUZHAOYANG
     * @date 2021/9/3 15:15
     **/
    @Override
    public void deleteCard(long id) {
        cardRepository.deleteById(id);
        reviewRepository.deleteByCardId(id);
        log.info("{} delete card, id={}", UserContext.getUser().getUserName(), id);
    }

    @Override
    public Card getOneCard(long id) {
        Optional<Card> cardOptional = cardRepository.findById(id);
        return cardOptional.orElse(null);
    }

    @Override
    public List<Card> getAll() {
        return cardRepository.findByUserId(UserContext.getUserId());
    }

    /**
     * 根据card id 查询卡片详情
     *
     * @param cardId
     * @return vip.redme.memory.pojo.dto.CardDetail
     * @author HUZHAOYANG
     * @date 2021/9/2 10:12
     **/
    @Override
    public CardDetail getOneCardDetail(long cardId) {
        Card card = cardRepository.findById(cardId).orElseThrow(of(RECORD_NOT_EXIST));
        Plan plan = planRepository.findById(card.getPlanId()).orElseThrow(of(RECORD_NOT_EXIST));
        Review review = reviewRepository.findByCardIdAndIsLatest(cardId, true);
        return CardDetail.build(card, plan, review);
    }

    /**
     * 查询一个用户下的所有卡片及其详情
     *
     * @return java.util.List<vip.redme.memory.pojo.dto.CardDetail>
     * @author HUZHAOYANG
     * @date 2021/8/30 17:25
     **/
    @Override
    public List<CardDetail> getAllCardDetail() {
        Long userId = UserContext.getUserId();
        List<Card> cards = cardRepository.findByUserId(userId);
        List<CardDetail> result = new ArrayList<>();
        if (!cards.isEmpty()) {
            List<Plan> plans = planRepository.findByUserId(userId);//todo redis缓存
            Map<Long, Plan> planCaseMap = plans.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));
            List<Review> reviews = reviewRepository.findByUserIdAndIsLatest(userId, true);
            Map<Long, Review> reviewMap = reviews.stream().collect(Collectors.toMap(Review::getCardId, Function.identity()));
            List<CardDetail> cardDetails = cards.stream()
                                                .map(e -> CardDetail.build(e, planCaseMap.get(e.getPlanId()), reviewMap.get(e.getId())))
                                                .collect(Collectors.toList());
            result.addAll(cardDetails);
        }

        return result;
    }


    /**
     * 更新Card
     *
     * @param card
     * @return void
     * @author HUZHAOYANG
     * @date 2021/9/2 10:29
     **/
    @Override
    public void update(Card card) {
        Card cardExist = cardRepository.findById(card.getId()).orElseThrow(of(RECORD_NOT_EXIST));
        BeanUtil.copyProperties(card, cardExist, CopyOptions.create().ignoreNullValue());
        cardRepository.saveAndFlush(cardExist);
    }

    /**
     * 复习了一张卡片
     *
     * @param id
     * @param reviewedType
     * @return void
     * @author HUZHAOYANG
     * @date 2021/9/2 11:09
     **/
    @Override
    public void reviewedOneCard(long id, ReviewedType reviewedType) {
        //update
        Card card = cardRepository.findById(id).orElseThrow(of(RECORD_NOT_EXIST));
        card.setReviewedTimes(card.getReviewedTimes() + 1);
        cardRepository.save(card);
        //update review record
        Review latest = reviewRepository.findByCardIdAndIsLatest(id, true);
        Plan plan = planRepository.findById(card.getPlanId()).orElseThrow(of(RECORD_NOT_EXIST));
        if (latest.getPlanTimes() < plan.getTotalTimes()) {
            //需要再次复习
            Review nextReview = BeanUtil.copyProperties(latest, Review.class, BaseEntity.IGNORE_PROPERTIES);
            List<Integer> schedule = plan.getSchedule();
            int planTimes = reviewedType.getNextPlanTimes(nextReview.getPlanTimes());
            nextReview.setPlanTimes(planTimes).setShouldReviewTime(LocalDateTime.now().plusHours(schedule.get(planTimes - 1)));
            reviewRepository.save(nextReview);
        }
        latest.setLatest(latest.getPlanTimes() == plan.getTotalTimes())//如果是复习计划最后一次,就让本条记录保持最新即可
              .setFinished(true)
              .setFinishedTime(LocalDateTime.now())
              .setReviewedType(reviewedType);
        reviewRepository.save(latest);
    }

    /**
     * 推迟复习
     *
     * @param id
     * @return void
     * @author HUZHAOYANG
     * @date 2021/9/3 16:00
     **/
    @Override
    public void delayReview(long id) {
        Review review = reviewRepository.findByCardIdAndIsLatest(id, true);
        review.setShouldReviewTime(LocalDateTime.now().plusHours(2));
        reviewRepository.save(review);
    }

    /**
     * 创建卡片时,创建一条复习记录
     *
     * @param card
     * @return void
     * @author HUZHAOYANG
     * @date 2021/9/2 11:10
     **/
    private void generateReviewForCreateCard(Card card) {
        Plan plan = planRepository.findById(card.getPlanId()).orElseThrow(of(RECORD_NOT_EXIST));
        List<Integer> schedule = plan.getSchedule();
        Review review = new Review().setUserId(UserContext.getUserId())
                                    .setCardId(card.getId())
                                    .setLatest(true)
                                    .setPlanTimes(1)
                                    .setShouldReviewTime(LocalDateTime.now().plusHours(schedule.get(0)));
        reviewRepository.saveAndFlush(review);
    }

}
